package com.weimob.mcloud.wechatwork.customer.utils;

import org.springframework.cglib.beans.BeanCopier;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 封装了cglib的BeanCopier并作缓存来提升bean拷贝的效率
 */
public class BeanCopierUtil {

    /**
     * 用来存放BeanCopier的缓存map
     */
    private static final Map<String, BeanCopier> BEAN_COPIER_CACHE_MAP = new ConcurrentHashMap<>();

    /**
     * 进行两个bean之间的properties拷贝
     * @param source 源bean
     * @param target 目标bean
     */
    public static void copyProperties(Object source, Object target) {
        BeanCopier copier = getBeanCopier(source.getClass(), target.getClass());
        copier.copy(source, target, null);
    }

    /**
     * 获取beanCopier
     * @param sourceClass
     * @param targetClass
     * @return
     */
    private static BeanCopier getBeanCopier(Class sourceClass, Class targetClass) {
        //生成key
        String beanKey = generateKey(sourceClass, targetClass);
        BeanCopier copier;
        if (!BEAN_COPIER_CACHE_MAP.containsKey(beanKey)) {
            //若缓存中没有则从map中获取
            copier = BeanCopier.create(sourceClass, targetClass, false);
            BEAN_COPIER_CACHE_MAP.put(beanKey, copier);
        } else {
            copier = BEAN_COPIER_CACHE_MAP.get(beanKey);
        }
        return copier;
    }

    /**
     * 两个类的全限定名拼接起来构成Key
     *
     * @param sourceClass
     * @param targetClass
     * @return
     */
    private static String generateKey(Class<?> sourceClass, Class<?> targetClass) {
        return sourceClass.getName() + targetClass.getName();
    }

    /**
     * bean拷贝并做类型转换
     * @param source 源bean
     * @param targetClass 目标bean的类型
     * @param <T>
     * @return 目标bean类型的bean
     */
    public static <T> T copyProperties(Object source, Class<T> targetClass) {
        T t;
        try {
            t = targetClass.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        copyProperties(source, t);
        return t;
    }
}
